home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / DevTools / eText5 / Source / Component.subproj / eTImageComponent.m < prev    next >
Encoding:
Text File  |  1995-02-04  |  14.4 KB  |  450 lines

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //    FILENAME:    eTImageComponent.m
  3. //    SUMMARY:    Implementation for a container of imageable data.
  4. //    SUPERCLASS:    Object
  5. //    INTERFACE:    None, but it "exports" eTImageComponentIcon.
  6. //    PROTOCOLS:    <ComponentData,
  7. //                ETFDSupport,HTMDSupport,LaTeXSupport>
  8. //    AUTHOR:        Rohit Khare
  9. //    COPYRIGHT:    (c) 1994 California Institure of Technology, eText Project
  10. ///////////////////////////////////////////////////////////////////////////////
  11. //    IMPLEMENTATION COMMENTS
  12. //        The concept is that eText systems only access NXImages through this
  13. //    component, which offers default services for image coercion and encoding.
  14. //
  15. //        The UI has the responsibility for passing in the desired filespec.
  16. //    Thus to keep a .pcx file as .pcx, pass in a complete path to the .pcx
  17. //    on disk and eText will either reference it or copy it as-is into the
  18. //    future (without filter-services, you will get eTImageComponentIcon.tiff)
  19. //        If the file cannot be found (or isDirty), eText will generate a .tiff
  20. //    and reset the names correctly.
  21. //
  22. //        Since this component maps onto an IMG tag, the only format with
  23. //    wide acceptance for IMG is GIF; so any non-gif file will be coerced.
  24. //    Similarly, LaTeX only has provisions for eps, so it will, too.
  25. //
  26. //    LaTeX and HTML representations will force the creation of local copies of
  27. //    images (this can/will change in the future, cf HTML OODB ideas)
  28. //
  29. //  shared images are currently envisioned for sysBitmaps like NXLinkButton
  30. //
  31. //    Note on HTML calling convention: this object maps exclusively onto the
  32. //    IMG tag, which can occur in the body of another anchor-like element.
  33. ///////////////////////////////////////////////////////////////////////////////
  34. //    HISTORY
  35. //    01/22/95:    Added call to handle caption strings.
  36. //    12/23/94:    Patched to always reset etDoc on read/write
  37. //    07/19/94:    Rearchitected
  38. //    07/14/94:    Created. Aliases the NeXTSTEP NXImage API.
  39. ///////////////////////////////////////////////////////////////////////////////
  40.  
  41. #import "eTImageComponent.h"
  42. #import "Kludges.subproj/EPSWriting.h"
  43. #import "Kludges.subproj/GIFWriting.h"
  44. #define _eTImageComponentVERSION    10
  45.  
  46. @implementation eTImageComponent
  47. //    NXImage    *theImage;
  48. //    BOOL    isShared;
  49.  
  50. + newImageNamed:(const char *)newName
  51. {
  52.     static id    _imageComponentTable=nil;
  53.     id newObj;
  54.     id newImg;
  55.     NXAtom theName;
  56.     
  57.     theName = NXUniqueString(newName);
  58.     if (!_imageComponentTable) {
  59.         _imageComponentTable = [[HashTable alloc] initKeyDesc:"%"];
  60.     }
  61.     newObj = [_imageComponentTable valueForKey:theName];
  62.     if (!newObj) {
  63.         if (newImg = [NXImage findImageNamed:theName]){    // system bitmap?
  64.             [newImg setScalable:NO];            // this is a shared image!
  65.             [newImg setDataRetained:YES];        // write TIFFs of sysBitmaps
  66.             [newImg setEPSUsedOnResolutionMismatch:YES];
  67.             // Here's a hack for dealing with recalcitrant alpha channels:
  68.              [newImg setBackgroundColor:NX_COLORWHITE];
  69.         } else {
  70.             newImg = [[NXImage alloc] init];
  71.             if (![newImg useFromFile:theName]){
  72.                 [newImg free]; 
  73.                 newImg = [NXImage findImageNamed:"eTImageComponentIcon"];
  74.             } else {
  75.                 // WHAT Happens to naming if theName is an absolute path?
  76.                 [newImg setName:theName];
  77.             }
  78.         }
  79.         
  80.         newObj = [[[eTImageComponent alloc] initInDoc:nil linked:NO]
  81.             useImage:newImg name:theName path:"" shared:YES];
  82.         [_imageComponentTable insertKey:theName value:newObj];
  83.     }
  84.     return newObj;
  85. }
  86. - theImage {return theImage;}
  87. - setComponentName:(const char*)newComponentName
  88.     {return (isShared ? nil : [super setComponentName:newComponentName]);}
  89. - (BOOL)isShared {return isShared;}
  90. - (BOOL)isMutable {return (!isShared && [super isMutable]);}
  91. //////// begin semi-private API section ///////
  92. - initInDoc:newDoc linked: (BOOL) linked
  93. {
  94.     [super initInDoc:newDoc linked:linked];
  95.     isShared = NO; shouldEdit = YES; theImage = nil;
  96.     return self;
  97. }
  98. - free
  99. {
  100.     if (isShared || [theImage name]) return self; // stop the free chain
  101.     theImage = [theImage free];
  102.     return self = [super free];
  103. }
  104.  
  105. - useImage:(NXImage *)newImage 
  106.       name:(const char *) newComponentName
  107.       path:(const char *) newCurrentPath
  108.     shared:(BOOL) newShared;
  109. {
  110.     theImage = newImage;
  111.     componentName = NXUniqueString(newComponentName);
  112.     currentPath = NXUniqueString(newCurrentPath);
  113.     isShared = newShared;
  114.     return self;
  115. }
  116.  
  117. - readComponentFromPath:(const char *)newPath
  118. {
  119.     if (newPath != currentPath) 
  120.         [super readComponentFromPath:newPath];
  121.     
  122.     // now we have filled in componentName/currentPath
  123.     // go out to disk and find the image
  124.     theImage = [[NXImage alloc] init];
  125.     [theImage setScalable:YES];
  126.     [theImage setDataRetained:YES];
  127.     [theImage setEPSUsedOnResolutionMismatch:YES];
  128.     // Here's a hack for dealing with recalcitrant alpha channels:
  129.     [theImage setBackgroundColor:NX_COLORWHITE];
  130.     
  131.     if (![theImage loadFromFile:currentPath]){
  132.         char temp[MAXPATHLEN];
  133.  
  134.         strcpy(temp, currentPath);
  135.         strcat(temp, ".eps");
  136.         if (![theImage loadFromFile:temp]){
  137.             strcpy(temp, currentPath);
  138.             strcat(temp, ".tiff");
  139.             if (![theImage loadFromFile:temp]){
  140.                 strcpy(temp, currentPath);
  141.                 [theImage free];
  142.                 // use eTImageComponentIcon.tiff to indicate an error
  143.                 theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
  144.                 isShared = YES;
  145.             }
  146.         }
  147.         currentPath = NXUniqueString(temp);
  148.     }
  149.     return self;
  150. }
  151. - writeComponentToPath:(NXAtom)path inFormat:(int) theFormat
  152. {
  153.     const char *tmp;
  154.     char target[MAXPATHLEN];
  155.     
  156.     if ((theFormat == ETFD_FMT) && (isShared || [theImage name]))
  157.         return self;
  158.     switch(theFormat) {
  159.         case ETFD_FMT:
  160.         //    if it's not (a link) or (clean and the target is found),
  161.         //        if the (source is found and it's clean), copy
  162.         //        else write.
  163.             if (isShared) break;    // special case for etfds.
  164.             sprintf(target,"%s/%s",path,componentName);
  165.             if (!((isLinked) || ((!isDirty) && (!access(target,F_OK|R_OK))))){
  166.                 if ((!isDirty) && *currentPath &&
  167.                     (!access(currentPath, F_OK|R_OK))) {
  168.                     // copy
  169.                     char cmd[2*MAXPATHLEN];
  170.  
  171.                     sprintf(cmd,"cp -rp \"%s\" \"%s\"", currentPath, target);
  172.                     system(cmd);
  173.                     currentPath = NXUniqueString(target);
  174.                 } else {
  175.                     id    repList;
  176.                     int    i,j;
  177.                     BOOL foundEPS=NO;
  178.                     NXStream *stream;
  179.  
  180.                     // choose format
  181.                     repList = [theImage representationList];
  182.                     i = [repList count];
  183.                     for(j=0; ((j<i) && !foundEPS); j++){
  184.                     if ([[repList objectAt:j] isKindOf:[NXEPSImageRep class]])
  185.                         foundEPS = YES;
  186.                     }        
  187.  
  188.                     stream = NXOpenMemory(NULL, 0, NX_READWRITE);
  189.                     if (foundEPS) {            
  190.                         [theImage writeEPS:stream];
  191.                         if (!rindex(target, '.') || 
  192.                             strcasecmp(".eps", rindex(target, '.')))
  193.                             strcat(target, ".eps");
  194.                     } else {
  195.                         [theImage writeTIFF:stream allRepresentations:YES];
  196.                         if (!rindex(target, '.') || 
  197.                             strncasecmp(".tif", rindex(target, '.'),3))
  198.                             strcat(target, ".tiff");
  199.                     }
  200.                     NXSaveToFile(stream, target);
  201.                     NXCloseMemory(stream, NX_FREEBUFFER);
  202.                     currentPath = NXUniqueString(target);
  203.                     componentName = NXUniqueString(rindex(target,'/')+1);
  204.                 }
  205.                 isDirty=NO;
  206.             }
  207.             [etDoc registerComponent:componentName];
  208.             break;
  209.         case HTMD_FMT:
  210.         // Always is a gif. If it doesn't exist, copy a .gif or create one.
  211.             if (componentName) tmp = componentName;
  212.             else if ([theImage name]) tmp = [theImage name];
  213.             else tmp = [[self class] name];
  214.  
  215.             sprintf(target,"%s/%s",path,tmp);
  216.             if(!rindex(target, '.') || strcasecmp(".gif", rindex(target, '.')))
  217.                 strcat(target, ".gif");
  218.             if (access(target,F_OK|R_OK) || isDirty) {
  219.                 // if currentPath is a gif, copy.
  220.                 if (rindex(currentPath, '.') &&
  221.                         (!strcasecmp(".gif", rindex(currentPath, '.'))) &&
  222.                         (!access(currentPath,F_OK|R_OK))) {
  223.                     char cmd[2*MAXPATHLEN];
  224.  
  225.                     sprintf(cmd,"cp -rp \"%s\" \"%s\"", currentPath, target);
  226.                     system(cmd);
  227.                 } else {
  228.                     [theImage writeGIFtoPath:target];
  229.                 }
  230.             }
  231.             [etDoc registerComponent:(rindex(target,'/')+1)];
  232.             break;
  233.         case TeXD_FMT:
  234.         // Always is an eps. If it doesn't exist, copy a .eps or create one.
  235.             if (componentName) tmp = componentName;
  236.             else if ([theImage name]) tmp = [theImage name];
  237.             else tmp = [[self class] name];
  238.             sprintf(target,"%s/%s",path,tmp);
  239.             if (!rindex(target, '.') || strcasecmp(".eps", rindex(target, '.')))
  240.                 strcat(target, ".eps");
  241.             if (access(target,F_OK|R_OK) || isDirty) {
  242.                 // if currentPath is an eps, copy.
  243.                 if ((rindex(currentPath, '.')) &&
  244.                         (!strcasecmp(".eps", rindex(currentPath, '.'))) &&
  245.                         (!access(currentPath,F_OK|R_OK))) {
  246.                     char cmd[2*MAXPATHLEN];
  247.  
  248.                     sprintf(cmd,"cp -rp \"%s\" \"%s\"", currentPath, target);
  249.                     system(cmd);
  250.                 } else {
  251.                     NXStream *stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  252.                     [theImage writeEPS:stream];
  253.                     NXSaveToFile(stream, target);
  254.                     NXCloseMemory(stream, NX_FREEBUFFER);
  255.                 }
  256.             }
  257.             [etDoc registerComponent:(rindex(target,'/')+1)];
  258.             break;
  259.     }
  260.     return self;
  261. }
  262.  
  263. - writeComponentToPboard:thePboard
  264. {
  265.     // I don't know why yet, but I want to defeat dragging of shared images
  266.     return (isShared ? nil : [super writeComponentToPboard:thePboard]);
  267. }
  268. - addToPboard:pboard
  269. {
  270.     id    repList;
  271.     int    i,j;
  272.     BOOL foundEPS=NO;
  273.     NXStream *stream;
  274.  
  275.     // we can let people copy them, though.???
  276.     if (!theImage) return nil;
  277.  
  278.     // choose format
  279.     repList = [theImage representationList];
  280.     i = [repList count];
  281.     for(j=0; ((j<i) && !foundEPS); j++){
  282.     if ([[repList objectAt:j] isKindOf:[NXEPSImageRep class]])
  283.         foundEPS = YES;
  284.     }        
  285.  
  286.     stream = NXOpenMemory(NULL, 0, NX_READWRITE);
  287.     if (foundEPS) {            
  288.         [theImage writeEPS:stream];
  289.         [pboard addTypes:&NXPostScriptPboardType num:1 owner:nil];
  290.         [pboard writeType:NXPostScriptPboardType fromStream:stream];
  291.     } else {
  292.         [theImage writeTIFF:stream allRepresentations:YES];
  293.         [pboard addTypes:&NXTIFFPboardType num:1 owner:nil];
  294.         [pboard writeType:NXTIFFPboardType fromStream:stream];
  295.     }
  296.     NXCloseMemory(stream, NX_FREEBUFFER);
  297.     return self;
  298. }
  299. - readComponentFromPboard:thePboard
  300. {
  301.     [super readComponentFromPboard:thePboard];
  302.     // ok, so now we have a quasi-valid entry in currentPath
  303.     // If the image can init from thePboard, go right ahead. 
  304.     
  305.     theImage = [[NXImage alloc] init];
  306.     [theImage setScalable:YES];
  307.     [theImage setDataRetained:YES];
  308.     [theImage setEPSUsedOnResolutionMismatch:YES];
  309.     // Here's a hack for dealing with recalcitrant alpha channels:
  310.      [theImage setBackgroundColor:NX_COLORWHITE];
  311.     
  312.     if ([NXImage canInitFromPasteboard:thePboard]) {
  313.         // Patch for recalcitrant f*king appkit
  314.         if([NXEPSImageRep canInitFromPasteboard:thePboard]) {
  315.             id epsRep = [[NXEPSImageRep alloc] initFromPasteboard:thePboard];
  316.             [theImage useRepresentation:epsRep];
  317.         } else 
  318.             theImage = [theImage initFromPasteboard:thePboard];
  319.         if (!theImage){
  320.             theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
  321.             isShared = YES;
  322.         }
  323.         // special case: If we paste raw image data and there's no path,
  324.         // componentName is currently "Untitled-%d"; we need to cat an ext.
  325.         // we should look at how this whole schmeer works/fails with RIBs.
  326.         if (!(currentPath && *currentPath)) {
  327.             char tmp[MAXPATHLEN];
  328.             
  329.             strcpy(tmp, componentName);
  330.             if([thePboard findAvailableTypeFrom:&NXPostScriptPboardType num:1])
  331.                 strcat(tmp, ".eps");
  332.             else if([thePboard findAvailableTypeFrom:&N3DRIBPboardType num:1])
  333.                 strcat(tmp, ".rib");
  334.             else strcat(tmp, ".tiff");
  335.             componentName = NXUniqueString(tmp);
  336.         }
  337.     } else if (currentPath && *currentPath) {
  338.         if (![theImage loadFromFile:currentPath]){
  339.             [theImage free]; 
  340.             theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
  341.             isShared = YES; 
  342.         }
  343.     } else {
  344.         theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
  345.         isShared = YES;
  346.     }
  347.     return self;
  348. }
  349.  
  350. - readRichText:(NXStream *)stream forView:view
  351. {
  352.     int i;
  353.     id temp;
  354.     
  355.     temp = [view etDoc];
  356.     if (temp && [temp respondsTo:@selector(registerComponent:)])
  357.         etDoc = temp; // Always reset if possible; bugfix for shared images, 12/23
  358.     NXScanf(stream, "%d ", &i);
  359.     if (i != _eTImageComponentVERSION) {
  360.         // bad version block.
  361.      NXLogError("eTImageComponent found unparseable version %d at position %d",
  362.                     i, NXTell(stream));
  363.         return nil;
  364.     }
  365.     NXScanf(stream, "%c ", &isShared); isShared -= 'A';
  366.     [super readRichText:stream forView:view];
  367.     if (isShared){
  368.         // we have a real problem here: we need to return the "uniq"d instance!
  369.         
  370.         id retval = [eTImageComponent newImageNamed:componentName];
  371.         [self free];
  372.         return retval;
  373.     }    
  374.     if (!isLinked) {
  375.         // derive a fresh currentPath. try to load.
  376.         char buf[MAXPATHLEN];
  377.         
  378.         sprintf(buf, "%s/%s",[[etDoc docInfo] docPath], componentName);
  379.         currentPath = NXUniqueString(buf);
  380.     }
  381.     [self readComponentFromPath:currentPath];
  382.     return self;
  383. }
  384. - writeRichText:(NXStream *)stream forView:view
  385. {
  386.     BOOL fakeShared;
  387.     id temp;
  388.     
  389.     temp = [view etDoc];
  390.     if (temp && [temp respondsTo:@selector(registerComponent:)])
  391.         etDoc = temp; // Always reset if possible; bugfix for shared images, 12/23
  392.     // this fixes a subtle bug related to images we can't locate at
  393.     // read-time and are assigned the eTImageComponentIcon by default
  394.     // (and thus set isShared) -- on reopen, it calls +newImageNamed with
  395.     // the old componentName
  396.     fakeShared = isShared;
  397.     if (([theImage name] == NXUniqueString("eTImageComponentIcon")) &&
  398.         strcmp([theImage name], componentName))
  399.         fakeShared = NO;
  400.     NXPrintf(stream, "%d %c ", _eTImageComponentVERSION, fakeShared+'A');
  401.     [super writeRichText:stream forView:view];
  402.     return self;
  403. }
  404.  
  405. - writeHTML:(NXStream *)stream forView:view {
  406.     return [self writeHTML:(NXStream *)stream forView:view withCaption:NULL];}
  407. - writeHTML:(NXStream *)stream forView:view withCaption:(NXAtom)caption
  408. {
  409.     const char *tmp;
  410.     id temp;
  411.     
  412.     temp = [view etDoc];
  413.     if (temp && [temp respondsTo:@selector(registerComponent:)])
  414.         etDoc = temp; // Always reset if possible; bugfix for shared images, 12/23
  415.     if (componentName) tmp = componentName;
  416.     else if ([theImage name]) tmp = [theImage name];
  417.     else tmp = [[self class] name];
  418.     NXPrintf(stream, "<IMG SRC=\"%s%s\" ALT=\"%v\">", tmp, 
  419.     ((!rindex(tmp, '.') || strcasecmp(".gif", rindex(tmp, '.')))?".gif":""),
  420.         (caption && *caption) ? caption : tmp);
  421.     return self;
  422. }
  423.  
  424.  
  425. - writeLaTeX:(NXStream *)stream forView:view
  426. {
  427.     const char *tmp;
  428.     id temp;
  429.     
  430.     temp = [view etDoc];
  431.     if (temp && [temp respondsTo:@selector(registerComponent:)])
  432.         etDoc = temp; // Always reset if possible; bugfix for shared images, 12/23
  433.     if (componentName) tmp = componentName;
  434.     else if ([theImage name]) tmp = [theImage name];
  435.     else tmp = [[self class] name];
  436.     NXPrintf(stream, "\n\\centerline{\\epsffile{%s%s}}\n",tmp, 
  437.     ((!rindex(tmp, '.') || strcasecmp(".eps", rindex(tmp, '.')))?".eps":""));
  438.     return self;
  439. }
  440.  
  441. // VERY PRIVATE API
  442. - setImage:newImg {theImage = newImg; return self;}
  443. - setShared:(BOOL)newState {isShared = newState; return self;}
  444. - registerError
  445. {
  446.     theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
  447.     isShared = YES;
  448.     return self;
  449. }
  450. @end